﻿// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using DocumentFormat.OpenXml.Generator.Models;
using System.CodeDom.Compiler;

namespace DocumentFormat.OpenXml.Generator.Editor;

public static class TextWriterExtensions
{
    internal static string ToFormattedString(this OfficeVersion version)
    {
        if (version == OfficeVersion.None)
        {
            return "None";
        }

        return (version == OfficeVersion.Microsoft365 ? string.Empty : "Office ") + version.ToString().Substring(version == OfficeVersion.Microsoft365 ? 0 : "Office".Length);
    }

    public static void WriteFileHeader(this TextWriter writer)
    {
        const string Line1 = "// Copyright (c) Microsoft. All rights reserved.";
        const string Line2 = "// Licensed under the MIT license. See LICENSE file in the project root for full license information.";

        writer.WriteLine("// <auto-generated/>");
        writer.WriteLine();
        writer.WriteLine(Line1);
        writer.WriteLine(Line2);
        writer.WriteLine();
        writer.WriteLine("#nullable enable");
        writer.WriteLine();
    }

    public static void WriteLineNoTabs(this IndentedTextWriter writer) => writer.WriteLineNoTabs(string.Empty);

    public static void WriteDoubleLines(this IndentedTextWriter writer)
    {
        writer.WriteLineNoTabs();
        writer.WriteLine();
    }

    public static Indentation AddIndent(this IndentedTextWriter writer, BlockOptions? options = null)
        => new(writer, options);

    public static Indentation AddBlock(this IndentedTextWriter writer, BlockOptions? options = null)
    {
        writer.WriteLine("{");
        var finalText = options?.FinalText is null ? "}" : options.FinalText + "}";

        return new(writer, options is null ? new() { FinalText = finalText } : options with { FinalText = finalText });
    }

    public static Indentation AddPropertyAccessor(this IndentedTextWriter writer, string accessor, BlockOptions? options = null)
    {
        writer.WriteLine(accessor);

        return writer.AddBlock(options);
    }

    public static void WriteEnum<T>(this TextWriter writer, string typeName, T value)
        where T : Enum
    {
        writer.Write(typeName);
        writer.Write(".");
        writer.Write(value.ToString());
    }

    public static void WriteJoin<T>(this TextWriter writer, string separator, IEnumerable<T> items)
    {
        var written = false;

        foreach (var item in items)
        {
            if (written)
            {
                writer.Write(separator);
            }

            written = true;
            writer.Write(item);
        }
    }

    public static void WriteList<T1, T2>(this TextWriter writer, T1 item1, T2 item2)
    {
        writer.Write("{ ");
        writer.WriteItem(item1);
        writer.Write(", ");
        writer.WriteItem(item2);
        writer.Write(" ");
        writer.Write("}");
    }

    public static void WriteList<T1, T2, T3>(this TextWriter writer, T1 item1, T2 item2, T3 item3)
    {
        writer.Write("{ ");
        writer.WriteItem(item1);
        writer.Write(", ");
        writer.WriteItem(item2);
        writer.Write(", ");
        writer.WriteItem(item3);
        writer.Write(" ");
        writer.Write("}");
    }

    public static void WriteItem<T>(this TextWriter writer, T item, bool isConstant = false)
    {
        if (item is null)
        {
            writer.WriteNull();
        }
        else if (typeof(T) == typeof(TypeOf))
        {
            writer.Write("typeof(");
            writer.Write(((TypeOf)(object)item).Type);
            writer.Write(")");
        }
        else if (typeof(T) == typeof(Verbatim))
        {
            writer.Write(((Verbatim)(object)item).Value);
        }
        else if (typeof(T) == typeof(OfficeVersion))
        {
            writer.WriteEnum("FileFormatVersions", (OfficeVersion)(object)item);
        }
        else if (typeof(T) == typeof(QName))
        {
            writer.WriteString(item.ToString());
        }
        else if (typeof(T) == typeof(string))
        {
            writer.WriteString((string)(object)item, isConstant);
        }
        else if (typeof(T) == typeof(bool))
        {
            var result = (bool)(object)item;
            writer.Write(result ? "true" : "false");
        }
        else
        {
            writer.Write(item.ToString());
        }
    }

    public static void WriteNull(this TextWriter writer) => writer.Write("null");

    public static void WriteBool(this TextWriter writer, bool value) => writer.Write(value ? "true" : "false");

    public static void WriteString(this TextWriter writer, string? input, bool isConstant = false)
    {
        if (input is null)
        {
            writer.WriteNull();
        }
        else if (input.Length == 0 && !isConstant)
        {
            writer.Write("string.Empty");
        }
        else
        {
            writer.Write("\"");
            writer.Write(input);
            writer.Write("\"");
        }
    }

    public static Delimiter TrackDelimiter(this IndentedTextWriter writer, string separator = ",", int newLineCount = 0)
        => new(writer, separator, newLineCount);
}
